<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="3c4e0a25" id="3c4e0a25"><span data-lake-id="u478a94d3" id="u478a94d3">典型回答</span></h1>
  <p data-lake-id="u4bef5e9a" id="u4bef5e9a"><br></p>
  <p data-lake-id="u8efc205b" id="u8efc205b"><span data-lake-id="u74dfbacd" id="u74dfbacd">DDL，即Data Defination Language，是用于定义数据库结构的操作。DDL操作用于创建、修改和删除数据库中的表、索引、视图、约束等数据库对象，而不涉及实际数据的操作。以下是一些常见的DDL操作：</span></p>
  <p data-lake-id="ubcc60221" id="ubcc60221"><span data-lake-id="u12916fb5" id="u12916fb5">​</span><br></p>
  <ul list="u7f84e75d">
   <li fid="u05e82226" data-lake-id="ua5625581" id="ua5625581"><span data-lake-id="ueed2f7f9" id="ueed2f7f9">CREATE </span></li>
   <li fid="u05e82226" data-lake-id="uc6bbe93f" id="uc6bbe93f"><span data-lake-id="udec90059" id="udec90059">ALTER </span></li>
   <li fid="u05e82226" data-lake-id="u88b43fe0" id="u88b43fe0"><span data-lake-id="u2fdd761b" id="u2fdd761b">DROP </span></li>
   <li fid="u05e82226" data-lake-id="uff1a92b3" id="uff1a92b3"><span data-lake-id="u69d0fd65" id="u69d0fd65">TRUNCATE</span></li>
  </ul>
  <p data-lake-id="ua2a73adf" id="ua2a73adf"><br></p>
  <blockquote data-lake-id="u77cf8c46" id="u77cf8c46">
   <p data-lake-id="u7611d2c8" id="u7611d2c8"><span data-lake-id="u06d43c9c" id="u06d43c9c">与DDL相对的是DML，即Data Manipulation Language，用于操作数据。即包括我们常用的INSERT、DELETE和UPDATE等。</span></p>
  </blockquote>
  <p data-lake-id="u1447fc32" id="u1447fc32"><span data-lake-id="u9787e31c" id="u9787e31c">​</span><br></p>
  <p data-lake-id="ub820917c" id="ub820917c"><span data-lake-id="u382cc1c7" id="u382cc1c7">在MySQL 5.6之前，所有的ALTER操作其实是会阻塞DML操作的，如：</span><span data-lake-id="uc98c742a" id="uc98c742a" style="color: rgb(0, 0, 0)">添加/删除字段、添加/删除索引等，都是会锁表的。</span></p>
  <p data-lake-id="u74d57972" id="u74d57972"><br></p>
  <p data-lake-id="ud8e20567" id="ud8e20567"><span data-lake-id="ue5796e6f" id="ue5796e6f">但是在MySQL 5.6中引入了Online DDL，OnLineDDL是MySQL5.6提出的加速DDL方案，</span><strong><span data-lake-id="u1e1cc97f" id="u1e1cc97f">尽最大可能</span></strong><span data-lake-id="u66608791" id="u66608791">保证DDL期间不阻塞DML动作。但是需要注意，这里说的</span><strong><span data-lake-id="u9abc0301" id="u9abc0301">尽最大可能</span></strong><span data-lake-id="u12cfd7e0" id="u12cfd7e0">意味着不是所有DDL语句都会使用OnlineDDL加速。</span></p>
  <p data-lake-id="u16997a37" id="u16997a37"><br></p>
  <p data-lake-id="u5e7ce27e" id="u5e7ce27e"><span data-lake-id="u4bb91dd0" id="u4bb91dd0">Online DDL的优点就是可以减少阻塞，是MySQL的一种内置优化手段，但是需要注意的是，DDL在刚开始和快结束的时候，都需要获取MDL锁，而在获取锁的时候如果有事务未提交，那么DDL就会因为加锁失败而进入阻塞状态，也会造成性能影响。</span></p>
  <p data-lake-id="uad20f9bc" id="uad20f9bc"><span data-lake-id="u68510b09" id="u68510b09">​</span><br></p>
  <p data-lake-id="uf495c021" id="uf495c021"><span data-lake-id="uca2e66ed" id="uca2e66ed">还有就是，如果Online DDL操作失败，其回滚操作可能成本较高。以及长时间运行的Online DDL操作可能导致主从同步滞后。</span></p>
  <h2 data-lake-id="dc9cb89a" id="dc9cb89a"><span data-lake-id="u47d17b58" id="u47d17b58">扩展阅读</span></h2>
  <p data-lake-id="ubf9faeb7" id="ubf9faeb7"><br></p>
  <h2 data-lake-id="WtRFy" id="WtRFy"><span data-lake-id="u59cc2a4d" id="u59cc2a4d">DDL算法</span></h2>
  <p data-lake-id="u188aed66" id="u188aed66"><br></p>
  <p data-lake-id="u4852ff7a" id="u4852ff7a"><span data-lake-id="u5bc5b30d" id="u5bc5b30d">在MySQL 5.6支持Online DDL之前，有两种DDL的算法，分别是COPY和INPLACE。</span></p>
  <p data-lake-id="u97d8d4b2" id="u97d8d4b2"><span data-lake-id="u78041765" id="u78041765">​</span><br></p>
  <p data-lake-id="u59c9fe99" id="u59c9fe99"><span data-lake-id="u029a68e6" id="u029a68e6">我们可以使用如下SQL指定DDL算法：</span></p>
  <p data-lake-id="ufe6a96c5" id="ufe6a96c5"><span data-lake-id="u86cff6b0" id="u86cff6b0">​</span><br></p>
  <pre lang="java"><code>
ALTER TABLE hollis_ddl_test ADD PRIMARY KEY (id) ,ALGORITHM=INPLACE,LOCK=NONE
</code></pre>
  <h3 data-lake-id="Walzz" id="Walzz"><span data-lake-id="u1114a127" id="u1114a127">copy算法原理</span></h3>
  <p data-lake-id="u30b5d9c7" id="u30b5d9c7"><br></p>
  <ol list="u9ebccd3a">
   <li fid="u1f4a904b" data-lake-id="u9ad567de" id="u9ad567de"><span data-lake-id="u775a85ea" id="u775a85ea">新建一张临时表</span></li>
   <li fid="u1f4a904b" data-lake-id="u7b9dd34b" id="u7b9dd34b"><span data-lake-id="u08c65bc4" id="u08c65bc4">对原表加共享MDL锁，禁止原表的写，只允许查询操作</span></li>
   <li fid="u1f4a904b" data-lake-id="u5a6a2cd1" id="u5a6a2cd1"><span data-lake-id="ud53ecf98" id="ud53ecf98">逐行拷贝原表数据到临时表，且不进行排序</span></li>
   <li fid="u1f4a904b" data-lake-id="ud5b3c989" id="ud5b3c989"><span data-lake-id="u67c8c260" id="u67c8c260">拷贝完成后升级原表锁为排他MDL锁，禁止原表读写</span></li>
   <li fid="u1f4a904b" data-lake-id="ua50d25cd" id="ua50d25cd"><span data-lake-id="u7ccfb43d" id="u7ccfb43d">对临时表rename操作，创建索引，完成DDL操作</span></li>
  </ol>
  <p data-lake-id="uf79bc38c" id="uf79bc38c"><br></p>
  <p data-lake-id="ubad828a2" id="ubad828a2"><span data-lake-id="u52e736dc" id="u52e736dc">​</span><br></p>
  <h3 data-lake-id="QHulK" id="QHulK"><span data-lake-id="u9c79615f" id="u9c79615f">INPLACE算法原理</span></h3>
  <p data-lake-id="u5c0c60d4" id="u5c0c60d4"><br></p>
  <p data-lake-id="u6e1f047e" id="u6e1f047e"><span data-lake-id="ua102e83e" id="ua102e83e">INPLACE算法是MySQL 5.1中引入的，主要是为了优化索引的创建和删除过程的效率。INPLACE算法的原理是可能地使用</span><strong><span data-lake-id="uf1f0bc10" id="uf1f0bc10">原地算法</span></strong><span data-lake-id="u313360fe" id="u313360fe">进行DDL操作，而不是重新创建或复制表。</span></p>
  <p data-lake-id="u0515f2b7" id="u0515f2b7"><span data-lake-id="ua9688836" id="ua9688836">​</span><br></p>
  <ol list="u6e5da199">
   <li fid="u1f4a904b" data-lake-id="u3213e581" id="u3213e581"><span data-lake-id="u6209a2fb" id="u6209a2fb">创建索引数据字典，</span></li>
   <li fid="u1f4a904b" data-lake-id="ucd87975e" id="ucd87975e"><span data-lake-id="u2bd3899f" id="u2bd3899f">对原表加共享MDL锁，禁止原表的写，只允许查询操作</span></li>
   <li fid="u1f4a904b" data-lake-id="uaf7087ec" id="uaf7087ec"><span data-lake-id="u64b41470" id="u64b41470">根据聚集索引的顺序，查询表中的数据，并提取需要的索引列数据。将提取的索引数据进行排序，并插入到新的索引页中。</span></li>
   <li fid="u1f4a904b" data-lake-id="u84563315" id="u84563315"><span data-lake-id="u15eba7c3" id="u15eba7c3">等待当前表的所有只读事务提交。</span></li>
   <li fid="u1f4a904b" data-lake-id="ue80fcb8f" id="ue80fcb8f"><span data-lake-id="u21952ff5" id="u21952ff5">创建索引结束。</span></li>
  </ol>
  <p data-lake-id="u3e2311f1" id="u3e2311f1"><br></p>
  <p data-lake-id="ua17e239e" id="ua17e239e"><span data-lake-id="u68211de9" id="u68211de9">MySQL中的INPLACE其实还可以分为以下两种算法：</span></p>
  <p data-lake-id="ubf9b7e1c" id="ubf9b7e1c"><span data-lake-id="ud7329107" id="ud7329107">​</span><br></p>
  <ul list="u7cd27a12">
   <li fid="u701e0b43" data-lake-id="u7e5441d8" id="u7e5441d8"><span data-lake-id="u522e748d" id="u522e748d">inplace-no-rebuild ：对二级索引的增删改查、修改变长字段长度（如：varchar）、重命名列名都不需要重建原表</span></li>
   <li fid="u701e0b43" data-lake-id="u19a126ca" id="u19a126ca"><span data-lake-id="u4f5084e3" id="u4f5084e3">inplace-rebuild：修改主键索引、增加删除列、修改字符集、创建全文索引等都需要重建原表。<br><br></span></li>
  </ul>
  <h2 data-lake-id="lV9Q5" id="lV9Q5"><span data-lake-id="uadbfaf21" id="uadbfaf21">OnlineDDL算法</span></h2>
  <p data-lake-id="ubbf81930" id="ubbf81930"><br></p>
  <p data-lake-id="u0823acd9" id="u0823acd9"><span data-lake-id="ueff4575f" id="ueff4575f">前面说过，ALGORITHM可以指定的DDL操作的算法，目前主要支持以下几种：</span></p>
  <p data-lake-id="uc5add492" id="uc5add492"><span data-lake-id="ub2c01e40" id="ub2c01e40">​</span><br></p>
  <ol list="u54f1ae04">
   <li fid="u608751b7" data-lake-id="u17dd775b" id="u17dd775b"><span data-lake-id="u7240d0a3" id="u7240d0a3">COPY算法</span></li>
   <li fid="u608751b7" data-lake-id="u21ad4ba1" id="u21ad4ba1"><span data-lake-id="ua7f658c5" id="ua7f658c5">INPLACE算法</span></li>
   <li fid="u608751b7" data-lake-id="ube11d472" id="ube11d472"><span data-lake-id="u06ef73e1" id="u06ef73e1">INSTANT算法：MySQL 8.0.12 引入的新算法，目前只支持添加列等少量操作，利用 8.0 新的表结构设计，可以直接修改表的元数据，省掉了重建原表的过程，极大的缩短了 DDL 语句的执行时间。其他类型的改表语句默认使用inplace算法。<br></span><span data-lake-id="uc025412c" id="uc025412c">关于instant支持的场景可参考官方文档</span><a href="https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html" target="_blank" data-lake-id="u1dca56e1" id="u1dca56e1"><span data-lake-id="u32ca4a75" id="u32ca4a75">Online DDL Operations</span></a></li>
   <li fid="u608751b7" data-lake-id="ub7ccc32e" id="ub7ccc32e"><span data-lake-id="u4f874e6e" id="u4f874e6e">DEFAULT：如果不指定ALGORITHM，那么MySQL会自行选择默认算法，优先使用INSTANT、其次是INPLACE、再然后是COPY</span></li>
  </ol>
  <p data-lake-id="u36f4dc10" id="u36f4dc10"><span data-lake-id="u54fb3c01" id="u54fb3c01">​</span><br></p>
  <p data-lake-id="u4ec1fdc9" id="u4ec1fdc9"><span data-lake-id="ua19f88b1" id="ua19f88b1">以下是MySQL官网上给出的Online DDL对索引操作的支持情况：</span></p>
  <p data-lake-id="u45a38f91" id="u45a38f91"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1699087940495-5f8f233a-7e00-4e6b-82d2-4de1bc796eff.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_51%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <h2 data-lake-id="Ie8Ji" id="Ie8Ji"><span data-lake-id="u3f1651e2" id="u3f1651e2">OnlineDDL的原理</span></h2>
  <p data-lake-id="ufcbfa2bf" id="ufcbfa2bf"><br></p>
  <p data-lake-id="u45c34772" id="u45c34772"><span data-lake-id="u231d7ee8" id="u231d7ee8">以下是OnlineDDL的整体步骤，主要分为Prepare阶段、DDL执行阶段以及Commit阶段。</span></p>
  <p data-lake-id="u1f42b53c" id="u1f42b53c"><br></p>
  <p data-lake-id="u59424a96" id="u59424a96"><strong><span data-lake-id="u50498a55" id="u50498a55">Prepare阶段：</span></strong></p>
  <p data-lake-id="u9ecfbfaf" id="u9ecfbfaf"><br></p>
  <ol list="u99f09514">
   <li fid="u9da9b0c4" data-lake-id="ube508541" id="ube508541"><span data-lake-id="ub1abf11c" id="ub1abf11c">创建临时 frm 文件</span></li>
   <li fid="u9da9b0c4" data-lake-id="u145f4c6c" id="u145f4c6c"><span data-lake-id="u85f4c913" id="u85f4c913">加EXCLUSIVE-MDL 锁，禁止读写</span></li>
   <li fid="u9da9b0c4" data-lake-id="uddc6a673" id="uddc6a673"><span data-lake-id="u2e113c17" id="u2e113c17">根据alter类型，确定执行方式（copy/online-rebuild/online-norebuild）。这里需要注意如果使用copy算法，就不是OnLineDDL了。</span></li>
   <li fid="u9da9b0c4" data-lake-id="u9939d5d8" id="u9939d5d8"><span data-lake-id="u09b1a11d" id="u09b1a11d">更新数据字典的内存对象</span></li>
   <li fid="u9da9b0c4" data-lake-id="u371b1947" id="u371b1947"><span data-lake-id="u138d39bc" id="u138d39bc">分配row_log对象，记录OnlineDDL过程中增量的DML</span></li>
   <li fid="u9da9b0c4" data-lake-id="uf888f5ed" id="uf888f5ed"><span data-lake-id="u66a94324" id="u66a94324">生成新的临时idb文件</span></li>
  </ol>
  <p data-lake-id="u4cbcfe55" id="u4cbcfe55"><br></p>
  <p data-lake-id="u2e8bbb7b" id="u2e8bbb7b"><strong><span data-lake-id="uc81cabd7" id="uc81cabd7">Execute阶段：</span></strong></p>
  <p data-lake-id="u72d14ad5" id="u72d14ad5"><br></p>
  <ol list="u2c5925de">
   <li fid="u8c2021ec" data-lake-id="ube6e4f89" id="ube6e4f89"><span data-lake-id="u98b1db7e" id="u98b1db7e">降级EXCLUSIVE-MDL锁为SHARED-MDL锁，允许读写。</span></li>
   <li fid="u8c2021ec" data-lake-id="uedfdcb46" id="uedfdcb46"><span data-lake-id="uc230812f" id="uc230812f">扫描原表聚集索引的每一条记录。</span></li>
   <li fid="u8c2021ec" data-lake-id="u4cb7e109" id="u4cb7e109"><span data-lake-id="u49966875" id="u49966875">遍历新表的聚集索引和二级索引，逐一处理。</span></li>
   <li fid="u8c2021ec" data-lake-id="u9fc30e30" id="u9fc30e30"><span data-lake-id="u94695f22" id="u94695f22">根据原表中的记录构造对应的索引项。</span></li>
   <li fid="u8c2021ec" data-lake-id="uc399ce66" id="uc399ce66"><span data-lake-id="u324eb0d5" id="u324eb0d5">将构造的索引项插入sort_buffer 块排序。</span></li>
   <li fid="u8c2021ec" data-lake-id="u7cd8b5bc" id="u7cd8b5bc"><span data-lake-id="u71f30f40" id="u71f30f40">将sort_buffer块更新到新表的索引上。</span></li>
   <li fid="u8c2021ec" data-lake-id="ud357b2cc" id="ud357b2cc"><span data-lake-id="uf0cb77cf" id="uf0cb77cf">记录 OnlineDDL 执行过程中产生的增量（oinline-rebuild）。</span></li>
   <li fid="u8c2021ec" data-lake-id="u7a2e6a11" id="u7a2e6a11"><span data-lake-id="u1bf8f4e6" id="u1bf8f4e6">重放row_log 中的操作到新表的索引上（online-not-rebuild 数据是在原表上更新）。</span></li>
   <li fid="u8c2021ec" data-lake-id="u0b3bd442" id="u0b3bd442"><span data-lake-id="u1f237648" id="u1f237648">重放row_log 中的 DML 操作到新表的数据行上。</span></li>
  </ol>
  <p data-lake-id="uc4690502" id="uc4690502"><br></p>
  <p data-lake-id="u6adf95ab" id="u6adf95ab"><strong><span data-lake-id="u08ce84d6" id="u08ce84d6">Commit阶段：</span></strong></p>
  <p data-lake-id="u46316408" id="u46316408"><br></p>
  <ol list="u68c5dac9" start="2">
   <li fid="ua14729ce" data-lake-id="ua909fac0" id="ua909fac0"><span data-lake-id="udc029697" id="udc029697">升级到 EXCLUSIVE-MDL 锁，禁止读写。</span></li>
   <li fid="ua14729ce" data-lake-id="u74b7916a" id="u74b7916a"><span data-lake-id="u1df9ee08" id="u1df9ee08">重做 row_log 中最后一部分增量。</span></li>
   <li fid="ua14729ce" data-lake-id="u029f5ce3" id="u029f5ce3"><span data-lake-id="ucd1d2080" id="ucd1d2080">更新 innodb 的数据字典表。</span></li>
   <li fid="ua14729ce" data-lake-id="u7970e8f6" id="u7970e8f6"><span data-lake-id="u3928bbff" id="u3928bbff">提交事务，写redolog。</span></li>
   <li fid="ua14729ce" data-lake-id="u65f3fa91" id="u65f3fa91"><span data-lake-id="u6cb2eec6" id="u6cb2eec6">修改统计信息。</span></li>
   <li fid="ua14729ce" data-lake-id="ud9da581a" id="ud9da581a"><span data-lake-id="ub05d31f5" id="ub05d31f5">rename 临时 ibd 文件，frm 文件。</span></li>
   <li fid="ua14729ce" data-lake-id="ue17655a3" id="ue17655a3"><span data-lake-id="u2369f8f4" id="u2369f8f4">变更完成，释放 EXCLUSIVE-MDL 锁。</span></li>
  </ol>
  <p data-lake-id="u0337f9f2" id="u0337f9f2"><br></p>
  <p data-lake-id="u6133cd9b" id="u6133cd9b"><span data-lake-id="ucbd7fbb4" id="ucbd7fbb4">Prepare阶段和Commit阶段虽然也加了EXECLUSIVE-MDL锁，但操作非常轻，所以耗时较低。Execute阶段允许读写，通过row_log记录期间变更的数据记录，最后再应用row_log到新表中。最终实现OnLineDDL的效果。</span></p>
 </body>
</html>